package com.quectel.business.common.parking.charging;

import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import com.google.common.base.Preconditions;
import com.quectel.business.common.parking.ex.ParkingChargingCalcException;
import com.quectel.constant.core.parking.ParkingChargingConstants;
import com.quectel.core.module.parking.dto.ParkingChargingDto;
import com.quectel.core.module.parking.dto.ParkingChargingSharingTimeDto;
import com.quectel.util.common.DateUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 在这里编写说明
 *
 * @author: ryan
 * @email: ryan.ran@quectel.com
 * @date: 2023-05-10 14:39:00
 */
public class OrderChargingCalcByTimeSharing extends BaseOrderChargingCalc{

    // 分时计费，单位计费时间，每30分钟xx元
    private static final BigDecimal CALC_FEE_UNIT_MINUTES = new BigDecimal("30");

    @Override
    public Byte calcType() {
        return ParkingChargingConstants.ChargingTypeEnum.TIME_SHARING.getChargingType();
    }

    @Override
    public BigDecimal calc(Date inTime, Date outTime, ParkingChargingDto chargingConf) {
        // 分时计算
        List<ParkingChargingSharingTimeDto> sharingTimeList =
                chargingConf.getSharingTimeList();

        if (CollectionUtils.isEmpty(sharingTimeList)) {
            throw new IllegalArgumentException("Parking charging sharing time calc config not found.");
        }
        // 分时计算有免费时长和首段基础收费
        if (Objects.nonNull(chargingConf.getFreeTime())
                && DateUtil.between(inTime, outTime, DateUnit.MINUTE, Boolean.FALSE) <= chargingConf.getFreeTime()) {
            return BigDecimal.ZERO;
        }
        Date realStartTime = DateUtil.offsetMinute(inTime, chargingConf.getBaseTime());
        if (realStartTime.getTime() > outTime.getTime()) {
            // 首段时长内出场，直接返回首段费用
            return chargingConf.getBasePrice();
        }

        Map<Byte, List<ParkingChargingSharingTimeDto>> sharingTimeByWeekendMap = chargingConf.getSharingTimeList().parallelStream()
                .sorted(Comparator.comparing(ParkingChargingSharingTimeDto::getStartTime))
                .collect(Collectors.groupingBy(ParkingChargingSharingTimeDto::getIsWeekend));

        BigDecimal result = BigDecimal.ZERO;
        BigDecimal partFee = chargingConf.getBasePrice();
        List<Pair<Date, Date>> splitByDayList = splitByDay(realStartTime, outTime);

        for (Pair<Date, Date> dateDatePair : splitByDayList) {
            List<ParkingChargingSharingTimeDto> calcConfList = null;
            if (ParkingChargingConstants.WeekendSiftEnum.SIFT_WEEKEND.getMode().equals(chargingConf.getSiftWeekend())
                    && DateUtil.isWeekend(dateDatePair.getLeft())) {
                // 需要区分周末，并且是周末
                calcConfList = sharingTimeByWeekendMap.get(ParkingChargingConstants.WeekendSiftEnum.SIFT_WEEKEND.getMode());
            }else{
                calcConfList = sharingTimeByWeekendMap.get(ParkingChargingConstants.WeekendSiftEnum.NO_SIFT_WEEKEND.getMode());
            }
            if (CollectionUtils.isEmpty(calcConfList)) {
                throw new ParkingChargingCalcException("Parking charging sharing time config is empty.");
            }

            // 开始计算
            Date partStartTime = dateDatePair.getLeft();
            for (ParkingChargingSharingTimeDto sharingTimeConfDto : calcConfList) {
                String dateStr = String.format("%s %s:00", DateUtils.format(partStartTime, DateUtils.FormatType.SIMPLE), sharingTimeConfDto.getEndTime());
                Date sharingTimeEndDate = DateUtils.parse(dateStr, DateUtils.FormatType.COMMON);
                Preconditions.checkNotNull(sharingTimeEndDate);

                if (sharingTimeEndDate.getTime() <= partStartTime.getTime()) {
                    continue;
                }
                if (sharingTimeEndDate.getTime() > dateDatePair.getRight().getTime()) {
                    sharingTimeEndDate = dateDatePair.getRight();
                }
                // 这里加一是因为截止时间是闭区间
                partFee = partFee.add(new BigDecimal(String.valueOf(DateUtil.between(partStartTime, sharingTimeEndDate
                        , DateUnit.MINUTE, Boolean.TRUE)))
                        .divide(CALC_FEE_UNIT_MINUTES, 0, RoundingMode.UP)
                        .multiply(sharingTimeConfDto.getUnitPrice()));
                partStartTime = sharingTimeEndDate;

                if (DateUtil.compare(partStartTime, dateDatePair.getRight()) >= 0) {
                    break;
                }
            }

            if (Objects.nonNull(chargingConf.getMaxCharge()) && partFee.compareTo(chargingConf.getMaxCharge()) > 0) {
                partFee = chargingConf.getMaxCharge();
            }
            result = result.add(partFee);

            partFee = BigDecimal.ZERO;
        }
        return result.setScale(2, RoundingMode.HALF_UP);
    }

    private List<Pair<Date, Date>> splitByDay(Date startTime, Date endTime){
        Date tempTime = startTime;
        if (startTime.getTime() > endTime.getTime()) {
            startTime = endTime;
            endTime = tempTime;
        }
        if (DateUtil.isSameDay(startTime, endTime)) {
            return Collections.singletonList(ImmutablePair.of(startTime, endTime));
        }

        tempTime = startTime;
        List<Date> dates = new ArrayList<>();
        do {
            tempTime = DateUtil.beginOfDay(DateUtil.offsetDay(tempTime, 1));
            dates.add(tempTime);
        } while (!DateUtil.isSameDay(tempTime, endTime) && tempTime.getTime() < endTime.getTime());
        dates.add(startTime);
        dates.add(endTime);
        dates.sort(Date::compareTo);

        List<Pair<Date, Date>> result = new ArrayList<>();
        tempTime = dates.get(0);
        for (int i = 1; i < dates.size(); i++) {
            result.add(ImmutablePair.of(tempTime, dates.get(i)));
            tempTime = dates.get(i);
        }
        return result;
    }
}
